//
// security.cpp
// libprotea
// Tools Library for the Protea Project / User and Group security
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include "../defines.h"
#include "../AppHandler/AppInterface.h"
#include "security.h"
#include <fcntl.h>
#include "../OSDep/misc.h"

//
// XCUserInformation class
//
XCUserInformation::XCUserInformation( const char* username, const char* userpassword, const char* user_descr, const int myindex ) {
	name = strdup( username );
	if (user_descr && *user_descr)
		description = strdup( user_descr );
	else
		description = NULL;
	password = userpassword ? strdup( userpassword ) : NULL;
	index = myindex;

//	printf( "Create user: %s, pass: %s, index:%d\n", name, password, index );
};

XCUserInformation::~XCUserInformation() {
//	printf( "Clear user information\n" );
	free( name );
	if (password)
		free( password );
	if (description)
		free( description );
};


//
// XCUserList class
//
XCUserList::XCUserList() {
	users = NULL;
	count = 0;
};

XCUserList::~XCUserList() {
//	printf( "Clear user list\n" );
	XCUserPointer* temp = users;
	while (temp) {
		XCUserPointer* deleteme = temp;
		temp = temp->next;
		delete deleteme->info;
		delete deleteme;
	};
};

XCUserInformation* XCUserList::Find( const char* name ) const {
	XCUserPointer* temp = users;
	while (temp) {
		if (XCStrCaseCompare( name, temp->info->name ) == 0)
			return temp->info;

		temp = temp->next;
	};

	return NULL;
};


XCUserInformation* XCUserList::Find( const int index ) const {
	XCUserPointer* temp = users;
	while (temp) {
		if (temp->info->index == index)
			return temp->info;

		temp = temp->next;
	};

	return NULL;
};


void XCUserList::Add( const char* name, const char* password, const char* description ) {
	XCUserPointer* temp = new XCUserPointer;
	temp->info = new XCUserInformation( name, password, description, ++count );
	temp->next = users;
	users = temp;
};


void XCUserList::Remove( const XCUserInformation* user ) {
	// See if this user is in the list
	XCUserPointer* ptr = users;
	XCUserPointer* deleteme = NULL;

	// Do we even have any users?
	if (ptr) {
		if (ptr->info->index == user->index) {
			// First item is it
			deleteme = users;
			users = users->next;
		} else {
			// Not the first item...
			while (ptr->next && ptr->next->info->index != user->index)
				ptr = ptr->next;

			if (ptr->next) {
				deleteme = ptr->next;
				ptr->next = ptr->next->next;
			};
		};

		if (deleteme) {
			count--;
			delete deleteme;
		};
	};
};




//
// Group
//
XCGroupInformation::XCGroupInformation( const char* group_name, const char* group_descr, const int myindex ) {
	name = strdup( group_name );
	if (group_descr && *group_descr)
		description = strdup( group_descr );
	else
		description = NULL;
	index = myindex;
	users = NULL;

//	printf( "Create group: %s, index:%d\n", group_name, index );
};

XCGroupInformation::~XCGroupInformation() {
//	printf( "Clear group info\n" );
	free( name );
	if (description)
		free( description );

    XCUserPointer* temp = users;
	while (temp) {
		XCUserPointer* deleteme = temp;
		temp = temp->next;
		delete deleteme;
	};
};

bool XCGroupInformation::ContainsUser( int user_index ) const {
	XCUserPointer* temp = users;
	while (temp) {
		if (temp->info->index == user_index)
			return true;
		temp = temp->next;
	};
	return false;
};

void XCGroupInformation::AddUser( XCUserInformation* info ) {
	if (info) {
//		printf( "Add group user: %s\n", info->name );

		XCUserPointer* temp = new XCUserPointer;
		temp->info = info;
		temp->next = users;
		users = temp;
	};
};

void XCGroupInformation::RemoveUser( const XCUserInformation* user ) {
	// See if this user is in this
	XCUserPointer* ptr = users;
	XCUserPointer* deleteme;

	// Do we even have any users?
	if (ptr) {
		if (ptr->info->index == user->index) {
			// First item is it
			deleteme = users;
			users = users->next;
		} else {
			// Not the first item...
			while (ptr->next && ptr->next->info->index != user->index) {
				ptr = ptr->next;
				if (ptr->next)
					printf( "Next item: %s (%d)\n", ptr->next->info->name, ptr->next->info->index );
			};

			if (ptr->next) {
				deleteme = ptr->next;
				ptr->next = ptr->next->next;
				delete deleteme;
			};
		};
	};
};



//
// GroupList
//
XCGroupList::XCGroupList() {
	groups = NULL;
	count = 0;
};

XCGroupList::~XCGroupList() {
//	printf( "Clear group list\n" );
	XCGroupInformation* temp = groups;
	while (temp) {
		XCGroupInformation* deleteme = temp;
		temp = temp->next_group;
		delete deleteme;
	};
};

XCGroupInformation* XCGroupList::Find( const char* name ) const {
	XCGroupInformation* temp = groups;
	while (temp) {
		if (strcmp( temp->name, name )==0)
			return temp;

		temp = temp->next_group;
	};

	return NULL;
};


XCGroupInformation* XCGroupList::Find( const int index ) const {
	XCGroupInformation* temp = groups;
	while (temp) {
		if (index == temp->index)
			return temp;

		temp = temp->next_group;
	};

	return NULL;
};


XCGroupInformation* XCGroupList::Add( const char* group_name, const char* description ) {
	XCGroupInformation* temp = new XCGroupInformation( group_name, description, ++count );
	temp->next_group = groups;
	groups = temp;

	return temp;
};


// Used to remove a group
void XCGroupList::Remove( const XCGroupInformation* group ) {
	if (groups == group) {
		// First item in the list
		groups = group->next_group;
	} else {
		XCGroupInformation* temp = groups;
		while (temp->next_group && temp->next_group != group)
			temp = temp->next_group;

		// Skip this group
		temp->next_group = group->next_group;
	};
};



// Used to remove a user from all groups
void XCGroupList::RemoveUser( const XCUserInformation* user ) {
	// For each group,
	XCGroupInformation* temp = groups;
	while (temp) {
		// Ask group to remove the user
		temp->RemoveUser( user );

		// Move on to next record
		temp = temp->next_group;
	};
};


XCGroupInformation* XCGroupList::GetGroupList() const {
	return groups;
};


////
//
// XCSecurityRule
//
////
XCSecurityRule::XCSecurityRule( int index, bool should_allow ) {
	allow = should_allow;
	user_group_index = index;
};




///
//
// SecurityList
//
///
XCSecurityRuleList::XCSecurityRuleList() {
	rules = NULL;
	default_allow = false;
};

XCSecurityRuleList::~XCSecurityRuleList() {
//	printf( "Clear security list\n" );
	XCSecurityRule* temp = rules;
	while (temp) {
		XCSecurityRule* deleteme = temp;
		temp = temp->next_rule;
		delete deleteme;
	};
};

void XCSecurityRuleList::Add( const XCAuthenticate* auth, const char* name, bool user, bool allow ) {
	INFUNC( XCSecurityRuleList::Add, NULL );

//	printf( "Add rule for %s '%s' - %s\n", user?"USER":"GROUP", name, allow?"ALLOW":"DENY" );

	// Determine the user index number
	int index;
	if (user) {
		XCUserInformation* user = auth->users.Find( name );
		if (user)
			index = user->index;
		else {
			// User not found, exit
			EXITFUNC();
		};
	} else {
		XCGroupInformation* group = auth->groups.Find( name );
		if (group)
			index = -group->index;
		else {
			// Group not found, exit
			EXITFUNC();
		};
	};

	// Add to the linked list
	if (!ContainsUser( index )) {
		XCSecurityRule* temp = new XCSecurityRule( index, allow );
		temp->next_rule = rules;
		rules = temp;
	} else
		RAISETOOLSERROR( 1 );

	OUTFUNC();
};

void XCSecurityRuleList::Remove( int index ) {
	XCSecurityRule* temp = rules;
	XCSecurityRule* deleteme;

	if (temp) {
		if (temp->user_group_index == index) {
			deleteme = rules;
			rules = rules->next_rule;
			delete deleteme;
		} else {
			while (temp->next_rule && temp->next_rule->user_group_index != index)
				temp = temp->next_rule;

			if (temp->next_rule) {
				deleteme = temp->next_rule;
				temp->next_rule = temp->next_rule->next_rule;
				delete deleteme;
				return;
			};
		};
	};
};


bool XCSecurityRuleList::ContainsUser( int user_index ) const {
	XCSecurityRule* temp = rules;
	while (temp) {
		if (temp->user_group_index == user_index)
			return true;
		// Move on to next rule
		temp = temp->next_rule;
	};
	return false;
};


int XCSecurityRuleList::Evaluate( const XCAuthenticate* auth, int user_index ) const {
	int result = 0;

//	printf( "Start eval:  Default '%s'\n", default_allow ? "ALLOW" : "DENY" );

	XCSecurityRule* temp = rules;
	while (temp) {
		// If this is the user index, return the results directly
		if (temp->user_group_index == user_index)
			return temp->allow ? 1 : -1;

		// This is a group, search for the user
		if (temp->user_group_index < 0) {
			XCGroupInformation* group = auth->groups.Find( -temp->user_group_index );
			if (group->ContainsUser( user_index )) {
				// Negative result always overrides a positive one
				if (temp->allow && result >= 0)
					result = 1;
				else
					result = -1;
			};
		};

		// Move on to next rule
		temp = temp->next_rule;
	};

	// If there were no rules for this user, then return the default
	if (!result) {
		result = default_allow ? 1 : -1;
//		printf( "Use default security: %d\n", result );
	};

	return result;
};




////
//
// XCSecurityItems class
//
////
XCSecurityItems::XCSecurityItems() {
	items = NULL;
};

XCSecurityItems::~XCSecurityItems() {
	XCSecurityItem* temp = items;
	while (temp) {
		XCSecurityItem* deleteme = temp;
		temp = temp->next;
		delete deleteme;
	};
};

XCSecurityItem* XCSecurityItems::AddItem( long number ) {
	XCSecurityItem* result = new XCSecurityItem( number );

	result->next = items;
	items = result;
	return result;
};

void XCSecurityItems::RemoveItem( const long index ) {
	Remove( index );

	XCSecurityItem* temp = items;
	while (temp) {
		temp->Remove( index );
		temp = temp->next;
	};
};

int XCSecurityItems::EvaluateItems( const XCAuthenticate* auth, int user_index, int number ) const {
	XCSecurityItem* temp = items;
	while (temp) {
		// If we have the transaction, then let it evaluate security rules
		if (temp->item_number == number)
			return temp->Evaluate( auth, user_index );
		temp = temp->next;
	};

	// Otherwise use the security setup for the "Items" catchall
	return Evaluate( auth, user_index );
};



////
//
// XCSecurityItem class
//
////
XCSecurityItem::XCSecurityItem( long number ) {
	item_number = number;
};





////
//
// XCSecurityApplication class
//
////
XCSecurityApplication::XCSecurityApplication( int app_number, bool default_allow_access ) {
	number = app_number;
	default_allow = default_allow_access;
};


void XCSecurityApplication::RemoveItem( const long index ) {
	// Remove the index from this application
	Remove( index );

	// Also remove the item from the application information
	trans.RemoveItem( index );
	caps.RemoveItem( index );
	data_items.RemoveItem( index );
};



////
//
// XCSecurityApplicationList class
//
////
XCSecurityApplicationList::XCSecurityApplicationList() {
	applist = NULL;
};

XCSecurityApplicationList::~XCSecurityApplicationList() {
	XCSecurityApplication* temp = applist;
	while (temp) {
		XCSecurityApplication* deleteme = temp;
		temp = temp->next;
		delete deleteme;
	};
};

void XCSecurityApplicationList::AddApplication( XCSecurityApplication* app ) {

	app->next = applist;
	applist = app;
};

XCSecurityApplication* XCSecurityApplicationList::GetApplication( int number ) const {
	XCSecurityApplication* temp = applist;
	while (temp && temp->number != number)
		temp = temp->next;

	return temp;
};

void XCSecurityApplicationList::RemoveItem( const long index ) {
	XCSecurityApplication* temp = applist;
	while (temp) {
		temp->RemoveItem( index );
		temp = temp->next;
	};
};


